

var doll = (function (_doll) {

    _doll.user = _doll.user || {};

    //A user is controlled by input from the outside world
    //simply put a player as opposed to something AI controlled

    //Flight user for mecha or fairie etc...
    _doll.user.flight = function(options) {
        var t = this;
        //options

        //character
        this.geo = null;
        this.mat = null;
        this.wiremat = null;
        this.mesh = null;
        this.wiremesh = null;

        this.world = null;//the world this user is connected to
        this.facing = new THREE.Vector3(0,0,-1);
        this.projector = new THREE.Projector();
        this.raycaster = new THREE.Raycaster();
        this.collide_distance = 32;

        //directions to check for collisions
        this.rays = [
            new THREE.Vector3(0, 0, 1),//forward
            new THREE.Vector3(0, 0, -1),//back
            new THREE.Vector3(1, 0, 0),//right
            new THREE.Vector3(-1, 0, 0),//left
            new THREE.Vector3(0, 1, 0),//up
            new THREE.Vector3(0, -1, 0),//down
        ];

        var RAY_FORWARD = 0;
        var RAY_BACK = 1;
        var RAY_RIGHT = 2;
        var RAY_LEFT = 3;
        var RAY_UP = 4;
        var RAY_DOWN = 5;


        //simple cursor should be replaced by system HUD
        this.cursor = null;
        this.cursor_mat = null;


        this.movebox_debug = new THREE.Mesh(new THREE.CubeGeometry(50, 50), 
                                            new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true}));
        this.movebox_debug.visible = false;

        this.lookbox_debug = new THREE.Mesh(new THREE.CubeGeometry(50, 50), 
                                            new THREE.MeshBasicMaterial({color: 0x00ff00, wireframe: true}));
        this.lookbox_debug.visible = false;

        //box with move orientation/debug
        this.movebox = new THREE.Object3D();
        this.movebox.add(this.movebox_debug);

        //box with look orientation/debug
        this.lookbox = new THREE.Object3D();
        this.lookbox.add(this.lookbox_debug);

        this.system_debug = new THREE.Mesh(new THREE.SphereGeometry(50,10,10),
                                  new THREE.MeshBasicMaterial({color: 0x0000ff, wireframe: true, side: THREE.BackSide}));
        this.system_debug.visible = false;

        //HUD texture is mapped to this sphere which is attached to system
        this.hud_sphere = new THREE.Mesh(new THREE.SphereGeometry(50,10,10),
                                  new THREE.MeshBasicMaterial({side: THREE.BackSide, 
                                                               transparent: true, opacity: 0.2}));

        //for stuff I dont want mapped to sphere like certain video or text
        this.hud_plane = new THREE.Mesh(new THREE.PlaneGeometry(50,50,1),
                                  new THREE.MeshBasicMaterial({side: THREE.BackSide,
                                                               transparent: true, opacity: 0.2}));

        //HUD info is on system is orients with the lookbox
        this.system = new THREE.Object3D();
        this.system.add(this.system_debug);
        this.system.add(this.hud_sphere);
        this.system.add(this.hud_plane);

        //various textures are made using canvas2d drawing libraries
        //and are then mapped to either hud_sphere or hud_plane

        //look direction
        this.lon = 0;
        this.lat = 0;

        //movement
        this.fthrust = 20;
        this.bthrust = 20;
        this.sthrust = 20;
        this.uthrust = 20;
        this.dthrust = 20;
        this.bank = 20;
        this.velocity = new THREE.Vector3(0,0,0);
        this.drag = new THREE.Vector3(0,0,0);//force opposite velocity
        this.forces = [];//list of forces acting on user

        //amount to turn before camera follows 0 seems to work best
        this.look = new THREE.Vector2(0,0);
        this.turn = new THREE.Vector2(0,0);

        //controls
        this.state = {
            forward: false,
            back: false,
            left: false,
            right: false,
            up: false,
            down: false,
            bank_left: false,
            bank_right: false,
            strafe: false,
            locked: false,
            turnx: false,
            turny: false
        };
        
        KEY.user_follow = KEY.f;
        KEY.user_forward = KEY.w;
        KEY.user_back = KEY.s;
        KEY.user_left = KEY.a;
        KEY.user_right = KEY.d;
        KEY.user_up = KEY.space;
        KEY.user_down = KEY.c;
        KEY.user_strafe = KEY.shift;
        KEY.user_bleft = KEY.q;
        KEY.user_bright = KEY.e;

        _doll.input.on_mousedown(function(m) {
            if(m.button == 1) {
                
            } else if(m.button == 3) {
                t.state.strafe = true;
                t.cursor.visible = false;
            }
        });

        _doll.input.on_mouseup(function(m) {
            if(m.button == 1) {
                
            } else if(m.button == 3) {
                t.state.strafe = false;
                t.cursor.visible = true;
            }
        });

        _doll.input.on_keydown(function(k) {
            if(t.world.camera.mode != t.world.camera.FLIGHT_CAM) {
                if(k.code == KEY.user_follow) {
                    t.world.camera.set_mode(t.world.camera.FLIGHT_CAM);
                    t.world.camera.flight.user = t;
                    t.world.scene.add(t.cursor);
                }
                return;
            }

            switch(k.code) {
            case KEY.user_forward:
                t.state.forward = true;
                break;
            case KEY.user_back:
                t.state.back = true;
                break;
            case KEY.user_left:
                t.state.left = true;
                break;
            case KEY.user_right:
                t.state.right = true;
                break;
            case KEY.user_up:
                t.state.up = true;
                break;
            case KEY.user_down:
                t.state.down = true;
                break;
            case KEY.user_blift:
                t.state.bank_left = true;
                break;
            case KEY.user_bright:
                t.state.bank_right = true;
                break;
            case KEY.user_follow:
                t.world.camera.set_mode(t.world.camera.FREE_CAM);
                t.world.camera.flight.user = null;
                t.world.scene.remove(t.cursor);
                break;
            case KEY.user_strafe:
                t.world.camera.lon = t.lon; //orbit to match
                t.world.camera.lat = t.lat; //orbit to match
                break;
            default:
                break;
            }
        });

        _doll.input.on_keyup(function(k) {
            switch(k.code) {
            case KEY.user_forward:
                t.state.forward = false;
                break;
            case KEY.user_back:
                t.state.back = false;
                break;
            case KEY.user_left:
                t.state.left = false;
                break;
            case KEY.user_right:
                t.state.right = false;
                break;
            case KEY.user_up:
                t.state.up = false;
                break;
            case KEY.user_down:
                t.state.down = false;
                break;
            case KEY.user_blift:
                t.state.bank_left = false;
                break;
            case KEY.user_bright:
                t.state.bank_right = false;
                break;
            default:
                break;
            }
        });

        //picking
        this.pick = function() {
            var c, i;
            var d = t.collide_distance;
            var rc = t.raycaster;
            var o = t.world.obstacles;

            console.log('phi', t.phi, '\tlon', t.lon, '\ntheta', t.theta, '\tlat', t.lat);

            //2d coordinates for interact with ui elements
            var c2d = new THREE.Vector3();
            c2d.copy(t.cursor.position);
            //pointer points up and to the left so adjust
            t.projector.projectVector(c2d, t.world.camera);

            var v = new THREE.Vector3().copy(t.cursor.position);
            //console.log('c2d', c2d, '\nc3d', v, '\ncursor', t.cursor.position, '\nuser', t.mesh.position);
            rc.set(t.world.camera.position, v.sub(t.world.camera.position).normalize());
            //rc.set(t.mesh.position, v.sub(t.mesh.position).normalize());
            c = rc.intersectObjects(o);
            
            if(c.length > 0) {
                for(var i=0; i<c.length; i++) {
                    if(c[i].object == ball) {
                        console.log('hit ball', c[i]);
                        ball.material.color.setHex( Math.random() * 0xffffff);
                    } else if(c[i].object == ball2) {
                        console.log('hit ball2', c[i]);
                        ball2.material.color.setHex( Math.random() * 0xffffff);
                    } else if(c[i].object == ball3) {
                        console.log('hit ball3', c[i]);
                        ball3.material.color.setHex( Math.random() * 0xffffff);
                    }
                }
            }
        }

        _doll.input.on_mousedown(function(mouse) {
            if(t.world.camera.mode == t.world.camera.FLIGHT_CAM && _doll.input.pointerlocked && mouse.button == 1) {
                t.pick();
            }
        });

        _doll.set_options(this, options);
      
        //check for collision
        this.collision = function() {
            var c, i;
            var d = t.collide_distance;
            var rc = t.raycaster;
            var rays = t.rays;
            var o = [];

            for(var i=0; i<t.world.obstacles.length; i++) {
                o[i] = t.world.obstacles[i].mesh;
            }

            if(!this.debug) {
                this.debug = true;
                console.log('obstacles',o,'\nrays', rays, '\nraycaster', rc);
            }

            for(i=0; i<rays.length; i++) {
                rc.set(t.mesh.position, rays[i]);
                c = rc.intersectObjects(o);

                if(c.length > 0 && c[0].distance <= d) {
                    //handle collision with closest object
                    switch(i) {
                    case RAY_DOWN: //land
                        t.velocity.y = 0;
                        break;
                    case RAY_UP:
                        t.velocity.y = 0;
                        break;
                    case RAY_FORWARD:
                        t.velocity.z = 0;
                        break;
                    case RAY_BACK:
                        t.velocity.z = 0;
                        break;
                    case RAY_LEFT: 
                        t.velocity.x = 0;
                        break;
                    case RAY_RIGHT:
                        t.velocity.x = 0;
                        break;
                    default:
                        break;
                    }
                }
            }
        }

        this.build_mesh = function() {

            //character mesh,geom, anim
            t.geo = new THREE.CubeGeometry(20,50,20);
            t.mat = new THREE.MeshBasicMaterial({color: 0xff00ff, transparent: true, opacity: 0.4});
            t.wiremat = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true});
            t.mesh = new THREE.Mesh(t.geo, t.mat);
            t.wiremesh = new THREE.Mesh(t.geo, t.wiremat);
            t.mesh.position.y += 25;
            t.mesh.rotation.y = 90 * _doll.RAD;

            //simple cursor sprite
            var tex = THREE.ImageUtils.loadTexture('assets/image/crosshair.png');
            t.cursor_mat = new THREE.SpriteMaterial({
                map: tex, 
                useScreenCoordinates: false,
                alignment: THREE.SpriteAlignment.center
            });
            t.cursor = new THREE.Sprite(t.cursor_mat);
            //cursor should be 70x50
            t.cursor.scale.set(70, 50, 1);           

        }
        
        this.wireframe = false;
        this.toggle_wireframe = function() {
            if(!t.wireframe) {
                t.wiremesh.position.copy(t.mesh.position);
                _doll.scene.add(t.wiremesh);
                t.wireframe = true;
            } else {
                _doll.scene.remove(t.wiremesh);
                t.wireframe = false;
            }
        }

        this.thrust_on = function() {
            return t.state.forward || t.state.back || t.state.left || t.state.right;
        }

        this.update = function (delta) {
            if(t.wireframe) {
                t.wiremesh.position.copy(t.mesh.position);
            }

            if(t.world.camera.mode != t.world.camera.FLIGHT_CAM) {
                return;
            }

            //t.mesh.lookAt(ball.position);//doesnt work when mouse is
            //on canvas maybe because ball is in a different scope or
            //file?
            //var look = new THREE.Vector3();
            //shouldn't this be -300? why does this work
            //_doll.orbit(look, t.mesh.position, 300, t.world.camera.phi, t.world.camera.theta);

            var mx,my;   
            mx = _doll.input.mouse.moveX;
            my = -_doll.input.mouse.moveY;

            if(t.state.strafe) {
                t.mesh.translateX(-mx);
                t.mesh.translateY(my);

                if(t.state.forward) {
                    t.mesh.translateZ(t.fthrust * 0.7);
                }

                if(t.state.back) {
                    t.mesh.translateZ(-t.bthrust * 0.7);
                }
            } else {
                if(t.state.forward) {
                    t.mesh.translateZ(t.fthrust);
                }

                if(t.state.back) {
                    t.mesh.translateZ(-t.bthrust);
                }

                if(t.state.left) {
                    t.mesh.translateX(t.sthrust);
                }

                if(t.state.right) {
                    t.mesh.translateX(-t.sthrust);
                }

                if(t.state.up) {
                    t.mesh.translateY(t.uthrust);
                }

                if(t.state.down) {
                    t.mesh.translateY(-t.dthrust);
                }

                //bank means rotate on Z axis
                if(t.state.bank_left) {
                    //t.mesh.translateY(-t.dthrust);
                }

                if(t.state.bank_right) {
                    //t.mesh.translateY(-t.dthrust);
                }

                var lsx = mx*6.0*delta;
                var lsy = -my*6.0*delta;
                
                var target_lon = t.lon + lsx;
                var target_lat = t.lat + lsy;
                var lon_diff = target_lon - t.world.camera.lon;
                var lat_diff = target_lat - t.world.camera.lat;

                if( (lon_diff > t.turn.x && mx > 0) || (lon_diff < -t.turn.x && mx < 0) )  {
                    //t.world.camera.lon = t.lon; //orbit to match
                    t.lon += lsx;
                    t.world.camera.lon += lsx; //turn camera
	            t.phi = t.lon * _doll.RAD;
                } else {
                    //t.world.camera.lon += lsx; //dont turn all the time
                    t.lon += lsx;
	            t.phi = t.lon * _doll.RAD;
                }

                if( (lat_diff > t.turn.y && my < 0) || (lat_diff < -t.turn.y && my > 0) ) {
                    //t.world.camera.lat = t.lat;//orbit to match
                    t.lat += lsy;
                    t.world.camera.lat += lsy; //turn camera
                    t.theta = ( 90 - t.lat ) * _doll.RAD;
                } else {
                    //t.world.camera.lat += lsy; //dont turn all the time
                    t.lat += lsy;
                    t.theta = ( 90 - t.lat ) * _doll.RAD;
                }

                //t.lat = Math.max( - 80, Math.min( 80, t.lat ) );
	        //t.lon = Math.max( - 80, Math.min( 80, t.lon ) );
                _doll.orbit(t.cursor.position, t.mesh.position, -300, t.phi, t.theta);
                t.mesh.lookAt(t.cursor.position);
            }
        };
    };//end user.flight

    return _doll;
}(doll || {}));

